Skip to content

feat: expose form transformations in evolution chains - #1658

Draft
MiquelRForgeFlow wants to merge 1 commit into
PokeAPI:masterfrom
MiquelRForgeFlow:feat/evolution-chain-transformations
Draft

MiquelRForgeFlow wants to merge 1 commit into
PokeAPI:masterfrom
MiquelRForgeFlow:feat/evolution-chain-transformations

Conversation

@MiquelRForgeFlow

Copy link
Copy Markdown
Contributor

Change description

/api/v2/evolution-chain/{id}/ only describes species-level evolution. The reversible form changes of those same species (mega evolution, primal reversion, gigantamax, in-battle transformations) live in pokemon_form_conditions.csv and are only reachable one variety at a time: pokemon-species/6 → varieties → pokemon/6 → forms → pokemon-form/10134 → trigger_conditions, i.e. 4 hops to learn that Charizard has two megas and a gmax form.

This PR adds a transformations field next to chain, grouping those conditions by species:

"transformations": [
  { "species": {"name": "charizard"},
    "forms": [
      {"form": {"name": "charizard-mega-x"}, "trigger": "held-item",
       "item": {"name": "charizardite-x"}, "ability": null, "move": null, "base_form": null},
      {"form": {"name": "charizard-gmax"}, "trigger": "gigantamax-factor", ...}
    ]
  }
]

Design notes:

  • chain is untouched. Form changes are not evolutions, so they are not chain links (otherwise Arceus would "evolve" into 17 plates and Aegislash into aegislash-blade). Purely additive: no model or migration changes.
  • Keys stay explicit (item / ability / move), unlike pokemon-form's trigger_conditions, which merges the target's {name, url} into the trigger dict and loses which of the three it came from.
  • transformations is always present, [] when the chain has no form conditions (399 of the 541 chains).
  • Costs exactly 1 extra query per request regardless of chain size.

AI coding assistance disclosure

It wrote the serializer and the tests from my design decisions.

Contributor check list

  • I have written a description of the contribution and explained its motivation.
  • I have written tests for my code changes (if applicable).
  • I have read and understood the AI Assisted Contribution guidelines.
  • I will own this change in production, and I am prepared to fix any bugs caused by my code change.

@FallenDeity

Copy link
Copy Markdown
Contributor

would adding a new field in pokemon-species > varieties itself not solve this problem?

{
 "is_default": false,
  "pokemon": {
    "name": "charizard-mega-x",
    "url": "https://pokeapi.co/api/v2/pokemon/10034/"
  },
  "pokemon_form": ...
}

I dont think reversible transformations should be added in whats considered the biological evolution chain

@MiquelRForgeFlow

MiquelRForgeFlow commented Aug 31, 2026 •

Copy link
Copy Markdown
Contributor Author

Agreed on the principle: reversible form changes aren't evolution, which is exactly why I kept them out of chain. You're taking that one step further, to the endpoint itself, and I'm fine with it. pokemon-species is the right home.

On putting it in varieties: it can't be a single pokemon_form object. A variety often holds more than one form, and that's where most of this data lives. arceus is one variety with 19 forms (18 plate conditions) and silvally one variety with 18 forms (17 memories). Six varieties also carry more than one condition on the same form (giratina-origin and zygarde-complete, with different triggers per generation). So it would have to be a list.

But once it's a list, nesting it under varieties still splits the data in a way that's awkward to read:

  • 26 of the 146 species that have conditions spread them across several varieties (zygarde across 6, plus aegislash, oricorio, wishiwashi), while arceus and silvally pile 18 into a single entry. The grouping doesn't track anything meaningful.
  • 37 conditions have a base_form pointing at a form that sits under a different variety. Reading a single pair (wishiwashi-solo to wishiwashi-school, darmanitan-zen to darmanitan-standard, greninja-ash to greninja-battle-bond) means hopping between entries of the same list.

So I'd rather put a flat list on the species itself:

"transformations": [
   {"form": {"name": "charizard-mega-x", "url": ".../pokemon-form/10134/"},
    "trigger": "held-item", "item": {"name": "charizardite-x", "url": "..."},
    "ability": null, "move": null, "base_form": null},
   {"form": {"name": "charizard-gmax", "url": ".../pokemon-form/10365/"},
    "trigger": "gigantamax-factor", "item": null, "ability": null, "move": null, "base_form": null}
 ]

Empty for 879 of the 1025 species, one extra query either way, and each pair stays readable in one place.

If you'd still prefer it under varieties, that's fine by me too. I'd just make it a list per variety instead of a single object. Let me know which one you want and I'll rework the PR.

@MiquelRForgeFlow

Copy link
Copy Markdown
Contributor Author

@FallenDeity Your answer?

Group the reversible form changes of a chain's species (mega evolution,
gigantamax, in-battle transformations) into a new `transformations` field,
instead of requiring one pokemon-form lookup per variety. `chain` is unchanged.
@MiquelRForgeFlow
MiquelRForgeFlow force-pushed the feat/evolution-chain-transformations branch from 7084b14 to ea7e3da Compare September 24, 2026 19:34
@FallenDeity

Copy link
Copy Markdown
Contributor

sorry was a bit busy yesterday will review today once i am back home

@FallenDeity

Copy link
Copy Markdown
Contributor
"transformations": [
   {"form": {"name": "charizard-mega-x", "url": ".../pokemon-form/10134/"},
    "trigger": "held-item", "item": {"name": "charizardite-x", "url": "..."},
    "ability": null, "move": null, "base_form": null},
   {"form": {"name": "charizard-gmax", "url": ".../pokemon-form/10365/"},
    "trigger": "gigantamax-factor", "item": null, "ability": null, "move": null, "base_form": null}
 ]

do we need the trigger duplicated here? was it not added to form a while back

from what i understand this is just going to be a skip bw species -> pokemon -> pokemon-form to direct species -> form

is it worth adding a new field just for shortcut/bypass its not too expensive of an api call imo feel like they would usually call pokemon endpoint anyways for sprites or some other data

feel like this adds a precedence for shortcut fields

cc: ur thoughts? @Naramsim @jemarq04

@MiquelRForgeFlow

Copy link
Copy Markdown
Contributor Author

On the duplication: fair, trigger_conditions has been on pokemon-form since #1578 and this repeats it.

On the cost, it is more than one skip. The condition lives on the form, so gathering a species' transformations today is 1 species call, plus one per variety, plus one per form: 21 requests for arceus (18 conditions), 20 for silvally (17), 29 for minior (14). The median is 5 across the 146 species that have any. And you cannot tell in advance which forms carry one: 879 of the 1025 species have none at all, and you only find that out after the full fan out.

On precedent, move.learned_by_pokemon, item.held_by_pokemon, stat.affecting_moves and location-area.pokemon_encounters are already reverse lookups whose primary home is another resource, so I would not call this a new category of field.

What I would like to settle first is whether the field is worth having at all, which is what your last comment is really asking and what @Naramsim and @jemarq04 are being asked. If the answer is yes, I am happy to discuss where it belongs on its own merits, with no attachment to any particular spot.

The commit currently on the PR is still the original evolution-chain version, so please read it as a sketch rather than a proposal. I am moving it to draft while we settle this, and I will rework it once the shape and the location are agreed.

@MiquelRForgeFlow
MiquelRForgeFlow marked this pull request as draft September 27, 2026 18:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants